﻿using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Dapper.Sharding.Maui
{
	public abstract class IClient
	{

		public IClient(DataBaseType dbType, DataBaseConfig config, DataBaseVersion version = DataBaseVersion.Default)
		{
			DbType = dbType;
			Config = config;
			DbVersion = version;
		}

		#region protected method

		protected readonly LockManager Locker = new LockManager();

		protected readonly LockManagerAsync LockerAsync = new LockManagerAsync();

		protected readonly ConcurrentDictionary<string, IDatabase> DataBaseCache = new ConcurrentDictionary<string, IDatabase>();

		protected readonly ConcurrentDictionary<int, IDatabase> DataBaseCache2 = new ConcurrentDictionary<int, IDatabase>();

		protected abstract IDatabase CreateIDatabase(string name);

		#endregion

		#region common method

		public DataBaseType DbType { get; }

		public DataBaseVersion DbVersion { get; }

		public DataBaseConfig Config { get; }

		public string Charset { get; set; }

		public bool AutoCreateDatabase { get; set; } = true;

		public bool AutoCreateTable { get; set; } = true;

		public bool AutoCompareTableColumn { get; set; } = true;

		public bool AutoCompareTableColumnDelete { get; set; } = false;

		public virtual IDatabase GetDatabase(string name)
		{
			var exists = DataBaseCache.TryGetValue(name, out var val);
			if (!exists)
			{
				lock (Locker.GetObject(name))
				{
					if (!DataBaseCache.ContainsKey(name))
					{
						if (AutoCreateDatabase)
						{
							if (!ExistsDatabase(name))
							{
								CreateDatabase(name);
							}
						}
						DataBaseCache.TryAdd(name, CreateIDatabase(name));
					}
				}
				val = DataBaseCache[name];
			}
			return val;
		}

		public virtual IDatabase GetDatabaseExists(string name)
		{
			var exists = DataBaseCache.TryGetValue(name, out var val);
			if (!exists)
			{
				lock (Locker.GetObject(name))
				{
					if (!DataBaseCache.ContainsKey(name))
					{
						DataBaseCache.TryAdd(name, CreateIDatabase(name));
					}
				}
				val = DataBaseCache[name];
			}
			return val;
		}

		public void ClearCache(string dbname = null)
		{
			if (!string.IsNullOrEmpty(dbname))
			{
				DataBaseCache.TryRemove(dbname, out _);
			}
			else
			{
				DataBaseCache.Clear();
			}
		}

		#endregion

		#region abstract method

		public abstract string ConnectionString { get; set; }


		public abstract void CreateDatabase(string name);

		public abstract void DropDatabase(string name);

		public abstract IEnumerable<string> ShowDatabases();

		public abstract IEnumerable<string> ShowDatabasesExcludeSystem();

		public abstract bool ExistsDatabase(string name);

		public abstract Task<bool> ExistsDatabaseAsync(string name);

		public abstract void Vacuum(string dbname);

        public abstract Task VacuumAsync(string dbname);

        #endregion

    }
}
